Skip to content

feat: replace libSQL/Turso with MongoDB Atlas for heartbeat storage#891

Merged
AchoArnold merged 10 commits into
mainfrom
feat/mongodb-heartbeat-backend
May 16, 2026
Merged

feat: replace libSQL/Turso with MongoDB Atlas for heartbeat storage#891
AchoArnold merged 10 commits into
mainfrom
feat/mongodb-heartbeat-backend

Conversation

@AchoArnold
Copy link
Copy Markdown
Member

Summary

Replace the libSQL/Turso database backend for heartbeat storage with MongoDB Atlas.

Changes

  • New files:

    • \�pi/pkg/repositories/mongodb.go\ — MongoDB Atlas connection helper with Stable API v1 and auto-index creation
    • \�pi/pkg/repositories/mongo_heartbeat_repository.go\ — \HeartbeatRepository\ implementation using MongoDB
    • \�pi/pkg/repositories/mongo_heartbeat_monitor_repository.go\ — \HeartbeatMonitorRepository\ implementation using MongoDB
  • Modified files:

    • \�pi/pkg/di/container.go\ — Added \MongoDB()\ client singleton; updated heartbeat repo switch cases to use MongoDB
    • \ ests/docker-compose.yml\ — Replaced \sqld\ (libSQL server) with \mongo:7\ container
    • \ ests/.env.test\ — Replaced \TURSO_DATABASE_DSN\ with \MONGODB_URI\

Configuration

\
HEARTBEAT_DB_BACKEND=hedging # PostgreSQL primary, MongoDB secondary (fail-open writes)
HEARTBEAT_DB_BACKEND=mongodb # MongoDB standalone
HEARTBEAT_DB_BACKEND= # PostgreSQL only (default, GORM)
\\

Requires \MONGODB_URI\ environment variable with the Atlas connection string.

Notes

  • The old libSQL repository files (\libsql_heartbeat_repository.go, \libsql_heartbeat_monitor_repository.go, \libsql.go) are no longer wired but remain in the codebase for reference. They can be removed in a follow-up cleanup PR.
  • The hedging pattern is preserved: PostgreSQL remains primary (reads + writes), MongoDB is the secondary (fail-open writes only).

- Add MongoDB Go driver v2 repository implementations for HeartbeatRepository
  and HeartbeatMonitorRepository interfaces
- Create mongodb.go connection helper with Atlas support and index creation
- Update DI container to wire MongoDB as the hedging secondary backend
- Replace 'turso' case with 'mongodb' case for standalone MongoDB usage
- Update integration test docker-compose to use mongo:7 instead of sqld
- Update .env.test with MongoDB connection string

HEARTBEAT_DB_BACKEND options:
  - 'hedging': PostgreSQL primary, MongoDB secondary (fail-open writes)
  - 'mongodb': MongoDB only
  - default: PostgreSQL only (GORM)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 16, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 45 complexity · 48 duplication

Metric Results
Complexity 45
Duplication 48

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

- Delete libsql.go, libsql_heartbeat_repository.go, libsql_heartbeat_monitor_repository.go
- Remove TursoDB() method and tursoDB field from DI container
- Remove unused database/sql import from container
- Run go mod tidy to remove libsql-client-go dependency

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 16, 2026

Greptile Summary

This PR replaces the libSQL/Turso secondary backend with MongoDB Atlas for heartbeat storage, adding three new repository files, updating the DI container, and swapping the test environment's sqld container for mongo:7.

  • New MongoDB repositories (mongodb.go, mongo_heartbeat_repository.go, mongo_heartbeat_monitor_repository.go) faithfully replicate the libSQL interface, using the same index shape and insert-only Store semantics as the GORM and libSQL counterparts.
  • DI container (container.go) gains a MongoDB() lazy singleton and both switch blocks replace the \"turso\" case with \"mongodb\".
  • Test infrastructure (docker-compose.yml, .env.test) swaps sqld for mongo:7 with a proper mongosh-based healthcheck.

Confidence Score: 3/5

The MongoDB repository logic is sound but two present defects — a credential leak in error logs and a broken documentation link — should be fixed before merging.

The new connection helper embeds the raw Atlas URI in a Ping error message that flows to the logger and Uptrace. The Vue settings page ships with a misspelled href that will 404 for every user clicking the Documentation button.

api/pkg/repositories/mongodb.go (URI in error message) and web/pages/settings/index.vue (broken documentation link)

Security Review

  • Credential exposure in logs (api/pkg/repositories/mongodb.go line 35): The raw MongoDB connection URI — which for Atlas connections embeds username and password — is included in the Ping error message. If connection fails at startup, the full credential string propagates to logger.Fatal and any connected error-tracking service (Uptrace is configured in this repo).

Important Files Changed

Filename Overview
api/pkg/repositories/mongodb.go New MongoDB connection helper — Stable API v1, Ping, and auto-index creation. Connection URI is included verbatim in the Ping error message, risking credential exposure in logs.
api/pkg/repositories/mongo_heartbeat_repository.go New MongoDB HeartbeatRepository — Store, Index, Last, and DeleteAllForUser implemented cleanly, consistent with GORM and libSQL counterparts.
api/pkg/repositories/mongo_heartbeat_monitor_repository.go New MongoDB HeartbeatMonitorRepository — full interface implemented, structurally consistent with GORM and libSQL counterparts.
api/pkg/di/container.go Adds MongoDB() singleton; swaps 'turso' case to 'mongodb' in both repository switch blocks.
tests/docker-compose.yml Replaces sqld with mongo:7; adds proper mongosh healthcheck.
tests/.env.test Replaces TURSO_DATABASE_DSN with MONGODB_URI; test-only credentials.
web/pages/settings/index.vue Adds Documentation button with a typo in the href ('outgiong') that produces a broken link.

Reviews (1): Last reviewed commit: "feat: replace libSQL/Turso with MongoDB ..." | Re-trigger Greptile

Comment thread api/pkg/repositories/mongodb.go Outdated
Comment on lines +34 to +35
if err = client.Ping(ctx, nil); err != nil {
return nil, stacktrace.Propagate(err, fmt.Sprintf("cannot ping MongoDB with URI [%s]", uri))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Connection URI exposed in error logs

The Ping error includes the raw uri parameter. Atlas connection strings embed credentials directly in the URI, so if Ping fails the full string with username and password propagates to container.logger.Fatal and any wired log/error aggregator (Uptrace is already configured in this codebase). Consider omitting the URI from this error message — the caller already knows which env var to check.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cdac79a. Removed the URI from the Ping error message so credentials won't leak to logs or Uptrace. The error now just says \cannot ping MongoDB Atlas\ — the caller knows which env var to check.

Comment thread api/pkg/repositories/mongodb.go Outdated
Comment on lines +23 to +43
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)

client, err := mongo.Connect(opts)
if err != nil {
return nil, stacktrace.Propagate(err, "cannot connect to MongoDB Atlas")
}

if err = client.Ping(ctx, nil); err != nil {
return nil, stacktrace.Propagate(err, fmt.Sprintf("cannot ping MongoDB with URI [%s]", uri))
}

if err = createMongoIndexes(ctx, client); err != nil {
return nil, stacktrace.Propagate(err, "cannot create MongoDB indexes")
}

return client, nil
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Shared context budget across Ping and index creation

A single 10-second context is created and then consumed sequentially by client.Ping and createMongoIndexes. On a cold Atlas connection, Ping can take several seconds, leaving less time for createMongoIndexes. If index creation is slow on first startup, the context will expire and startup will fail. Each operation should use its own independently-bounded context.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in cdac79a. Separated into independent contexts: \pingCtx\ with 10s timeout and \indexCtx\ with 30s timeout. Each operation now has its own time budget so a slow Ping won't starve index creation.

AchoArnold and others added 4 commits May 16, 2026 13:14
…ent types

- Add bson struct tags to entities.Heartbeat and entities.HeartbeatMonitor
- Register custom UUID codec so uuid.UUID is stored as string _id in MongoDB
- Remove intermediate heartbeatDocument/heartbeatMonitorDocument structs
- MongoDB repositories now marshal/unmarshal entities directly

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove hardcoded mongoDBName constant
- Parse appName query parameter from MONGODB_URI as the database name
- NewMongoDB now returns (client, dbName, error)
- Repository constructors accept dbName parameter
- DI container caches and passes the DB name to repositories
- Update test .env to include appName=httpsms in URI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- NewMongoDB now returns *mongo.Database instead of (*mongo.Client, dbName)
- Repository constructors accept *mongo.Database instead of client + dbName
- DI container caches the *mongo.Database singleton directly
- Removes MongoDBName() helper - no longer needed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix typo in URL: outgiong -> outgoing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@NdoleStudio NdoleStudio deleted a comment from greptile-apps Bot May 16, 2026
AchoArnold and others added 4 commits May 16, 2026 13:26
Use otelmongo.NewMonitor() as the command monitor on the MongoDB
client to automatically trace all database operations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove MongoDB URI from Ping error message to prevent credential
  exposure in logs and error aggregators
- Use separate contexts for Ping (10s) and index creation (30s) so
  they don't share a timeout budget

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the sqld health check with a MongoDB mongosh ping check
since the test infrastructure now uses mongo:7 instead of sqld.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AchoArnold AchoArnold merged commit a472abf into main May 16, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant